home *** CD-ROM | disk | FTP | other *** search
/ Sprite 1984 - 1993 / Sprite 1984 - 1993.iso / src / lib / c / etc / fsDispatch.c < prev    next >
C/C++ Source or Header  |  1990-03-29  |  25KB  |  928 lines

  1. /* 
  2.  * fsDispatch.c --
  3.  *
  4.  *    This file contains routines that implement a dispatcher for events
  5.  *    on streams and timeouts. The dispatcher handles the details of
  6.  *    waiting for events to occur on streams. When an event occurs, the
  7.  *    dispatcher calls a routine supplied by the clients to handle the
  8.  *    event. Also, timeout handlers can be created so that a client-supplied
  9.  *    routine can be called at a specific time or at regular intervals.
  10.  *
  11.  * Copyright 1987 Regents of the University of California
  12.  * Permission to use, copy, modify, and distribute this
  13.  * software and its documentation for any purpose and without
  14.  * fee is hereby granted, provided that the above copyright
  15.  * notice appear in all copies.  The University of California
  16.  * makes no representations about the suitability of this
  17.  * software for any purpose.  It is provided "as is" without
  18.  * express or implied warranty.
  19.  */
  20.  
  21. #ifndef lint
  22. static char rcsid[] = "$Header: /sprite/src/lib/c/etc/RCS/fsDispatch.c,v 1.7 89/11/22 16:34:29 ouster Exp $ SPRITE (Berkeley)";
  23. #endif not lint
  24.  
  25. #include <sprite.h>
  26. #include <errno.h>
  27. #include <fs.h>
  28. #include <list.h>
  29. #include <bit.h>
  30. #include <spriteTime.h>
  31. #include <sys/time.h>
  32. #include <stdio.h>
  33. #include <stdlib.h>
  34. #include <string.h>
  35.  
  36.  
  37. /*
  38.  * The data structure used by Fs_EventHandler{Create,Destroy} and Fs_Dispatch 
  39.  * to manage event handlers for streams. The information is kept in an
  40.  * array that is indexed by the stream ID.
  41.  *
  42.  */
  43.  
  44. typedef struct {
  45.     void    (*proc)();    /* Routine to be called. */
  46.     ClientData    data;        /* Data passed to proc. */
  47.     int        eventMask;    /* Mask of events cause proc to be called. */
  48.     Boolean    inUse;        /* Consistency check. */
  49. } StreamInfo;
  50.  
  51. #define MAX_NUM_STREAMS        256
  52. static StreamInfo infoArray[MAX_NUM_STREAMS];
  53.  
  54.  
  55. /*
  56.  * Bit arrays for use with select. ReadMask, writeMask and exceptMask
  57.  * are the master copies of the masks -- they are updated by 
  58.  * Fs_EventHandlerCreate and Fs_EventHandlerDestroy.
  59.  */
  60.  
  61. #define MASK_SIZE    (Bit_NumInts(MAX_NUM_STREAMS))
  62. static int    readMask[MASK_SIZE];
  63. static int    writeMask[MASK_SIZE];
  64. static int    exceptMask[MASK_SIZE];
  65.  
  66. /*
  67.  * MaxPossNumStreams is the maximum value for the number of active streams. 
  68.  * For example if streams 0, 1, and 5 are the only active streams, 
  69.  * maxPossNumStreams should equal to 6 because streams 0-5 could active.
  70.  */
  71. static int    maxPossNumStreams = -1;
  72.  
  73.  
  74. /*
  75.  * The data structure used by Fs_TimeoutHandler{Create,Destroy},
  76.  * CallTimeoutHandler and ComputeTimeoutValue to manage timeout handlers.
  77.  * The information is kept in a doubly-linked list that is sorted on 
  78.  * the time field.
  79.  */
  80.  
  81. typedef struct {
  82.     List_Links    links;
  83.     void    (*proc)();    /* Routine to be called. */
  84.     ClientData    data;        /* Data passed to proc. */
  85.     Time    time;        /* Absolute time when proc should be called. */
  86.     Time    interval;    /* Relative time when proc should be called. */
  87.     Boolean    resched;    /* If TRUE, reschedule the proc to be called
  88.                  * again at the next interval. */
  89. } TimeoutInfo;
  90. static List_Links    timeoutList;
  91.  
  92. /*
  93.  * Forever is the amount of time to wait until the next timeout
  94.  * (a very, very long time).
  95.  */
  96. static Time forever = { 0x7fffffff, 0 };
  97.  
  98. /*
  99.  * MIN_TIMEOUT is the # of microseconds that must past before the next
  100.  * call to a timeout handler, i.e. the minimum interval between calls to
  101.  * timeout handler. This is used to make sure the dispatcher doesn't
  102.  * spend all its time handling timeouts.
  103.  *
  104.  * 40000 microseconds = 40 milliseconds.
  105.  */
  106.  
  107. #define MIN_TIMEOUT  40000
  108.  
  109. /*
  110.  * A flag to indicate if DispatcherInit() has been called or not.
  111.  */
  112. static Boolean initialized = FALSE;
  113.  
  114. /*
  115.  * Statistics about what type of event (stream or timeout) occurs
  116.  * during each call to Fs_Dispatch().
  117.  */
  118. unsigned int fsNumTimeoutEvents = 0;
  119. unsigned int fsNumStreamEvents = 0;
  120.  
  121. /*
  122.  * Forward references.
  123.  */
  124.  
  125. static void    SearchMask();
  126. static void    CallTimeoutHandler();
  127. static Boolean    ComputeTimeoutValue();
  128. static void    DispatcherInit();
  129.  
  130.  
  131. /*
  132.  *----------------------------------------------------------------------
  133.  *
  134.  * DispatcherInit --
  135.  *
  136.  *    Initializes the data structures necessary to manage the event handlers,
  137.  *    the timer queue of procedures and the select bitmasks. 
  138.  *
  139.  * Results:
  140.  *    None.
  141.  *
  142.  * Side effects:
  143.  *    The infoArray and timer queue list are initialized.
  144.  *    The select bitmasks are set to 0.
  145.  *
  146.  *----------------------------------------------------------------------
  147.  */
  148.  
  149. static void
  150. DispatcherInit()
  151. {
  152.     initialized = TRUE;
  153.  
  154.     bzero((char *) infoArray, sizeof(StreamInfo) * MAX_NUM_STREAMS);
  155.     List_Init(&timeoutList);
  156.  
  157.     Bit_Zero(MAX_NUM_STREAMS, readMask);
  158.     Bit_Zero(MAX_NUM_STREAMS, writeMask);
  159.     Bit_Zero(MAX_NUM_STREAMS, exceptMask);
  160. }
  161.  
  162.  
  163. /*
  164.  *----------------------------------------------------------------------
  165.  *
  166.  * Fs_Dispatch --
  167.  *
  168.  *      This routine calls select to wait for a timeout or for a stream
  169.  *      to become readable, writable and/or has an exception condition
  170.  *      pending.  It looks at the results returned by select, and calls
  171.  *      client routines to handle the events.  Client routines must have
  172.  *      been pre-registered by calling Fs_EventHandlerCreate or
  173.  *      Fs_TimeoutHandlerCreate.
  174.  *
  175.  *    The readiness event handlers are called in ascending order 
  176.  *    based on the stream ID, regardless of the order of calls to 
  177.  *    Fs_EventHandlerCreate. However, the timeout handlers are
  178.  *      called in the order determined by the ordering of calls
  179.  *      to Fs_TimeoutHandlerCreate.
  180.  *
  181.  *    The routine will return after handling the events from a
  182.  *    single select call.
  183.  *
  184.  * Results:
  185.  *    None.
  186.  *
  187.  * Side effects:
  188.  *    The called routines may cause side-effects.
  189.  *
  190.  *----------------------------------------------------------------------
  191.  */
  192.  
  193. void
  194. Fs_Dispatch()
  195. {
  196.     register int    streamID;
  197.     Time        timeout;
  198.     Time        *timeoutPtr;
  199.     int            numReady;
  200.     int            tempReadMask[MASK_SIZE];
  201.     int            tempWriteMask[MASK_SIZE];
  202.     int            tempExceptMask[MASK_SIZE];
  203.  
  204.     if (!initialized) {
  205.     panic("Fs_Dispatch: not initialized yet.\n");
  206.     }
  207.  
  208.     /*
  209.      * First compute how time must elapse before the next routine
  210.      * on the timeout queue should be called. If ComputeTimeoutValue()
  211.      * determines that a routine should be called immediately, it
  212.      * returns TRUE and we go to CallTimeoutHandler() to call the routine.
  213.      */
  214.     
  215.     if (ComputeTimeoutValue(&timeout)) {
  216.     CallTimeoutHandler();
  217.     fsNumTimeoutEvents++;
  218.     return;
  219.     }
  220.  
  221.     if (Time_EQ(timeout, forever)) {
  222.     /*
  223.      * Nothing on the timeout queue.
  224.      */
  225.  
  226.     if (maxPossNumStreams == 0) {
  227.         panic("Fs_Dispatch: nothing to do.\n");
  228.         return;
  229.     } else {
  230.         timeoutPtr = (Time *) NULL;
  231.     }
  232.     } else {
  233.     if (maxPossNumStreams == 0) {
  234.         /*
  235.          * No streams to select so just wait until the next routine
  236.          * on the timeout queue needs to be called.
  237.          */
  238.  
  239.         select(0, (int *) NULL, (int *) NULL, (int *) NULL,
  240.             (struct timeval *) &timeout);
  241.         CallTimeoutHandler();
  242.         fsNumTimeoutEvents++;
  243.         return;
  244.     } else {
  245.         timeoutPtr = &timeout;
  246.     }
  247.     }
  248.  
  249.  
  250.     /*
  251.      * Wait for an event on 1 or more streams or until a timeout
  252.      * period has expired.
  253.      */
  254.  
  255.     Bit_Zero(MAX_NUM_STREAMS, tempReadMask);
  256.     Bit_Zero(MAX_NUM_STREAMS, tempWriteMask);
  257.     Bit_Zero(MAX_NUM_STREAMS, tempExceptMask);
  258.  
  259.     Bit_Copy(maxPossNumStreams, readMask, tempReadMask);
  260.     Bit_Copy(maxPossNumStreams, writeMask, tempWriteMask);
  261.     Bit_Copy(maxPossNumStreams, exceptMask, tempExceptMask);
  262.  
  263.  
  264.     numReady = select(maxPossNumStreams, tempReadMask, tempWriteMask,
  265.         tempExceptMask, (struct timeval *) timeoutPtr);
  266.  
  267.     if (numReady == 0) {
  268.     /*
  269.      * Nothing happened on the streams but a routine in the timeout
  270.      * queue needs to be called now.
  271.      */
  272.     CallTimeoutHandler();
  273.     fsNumTimeoutEvents++;
  274.  
  275.     } else if (numReady < 0) {
  276.     if (errno != EINTR) {
  277.         fprintf(stderr, "Fs_Dispatch select error: %s\n", strerror(errno));
  278.         exit(1);
  279.     }
  280.  
  281.     } else {
  282.     register int    event;
  283.     register StreamInfo    *infoPtr;
  284.  
  285.     fsNumStreamEvents++;
  286.  
  287.     /*
  288.      * Something happened on a stream (or streams). Go through
  289.      * the masks to find out which streams need attention.
  290.      * Call the routine to handle the event on the stream.
  291.      *
  292.      * We want to call the handler just once so when searching through
  293.      * the read mask, see if the stream is also writable and has an
  294.      * exception condition pending, and likewise, in the writable mask
  295.      * search see if the stream also has an exception condition pending.
  296.      * This ensures that a handler is called only once, with the
  297.      * eventMask containing 1 to 3 events. A simpler search technique
  298.      * is to search the 3 masks independently but then a handler could
  299.      * potentially be called up to 3 times. The first technique is
  300.      * better because the handler can determine the order of how it
  301.      * handles the events.
  302.      */
  303.  
  304.     streamID = Bit_FindFirstSet(maxPossNumStreams, tempReadMask);
  305.     while (streamID != -1) {
  306.         if (streamID < 0 || streamID > MAX_NUM_STREAMS) {
  307.         panic("Fs_Dispatch: stream ID %d out of range\n", streamID);
  308.         }
  309.  
  310.         infoPtr = &(infoArray[streamID]);
  311.  
  312.         /*
  313.          * There used to be code here to check inUse and panic
  314.          * if not set.  However, it's possible that one event
  315.          * handler could delete another event handler, causing
  316.          * inUse to be turned off in the second one before we
  317.          * get to this point.  Thus, don't check inUse here.
  318.          */
  319.  
  320.         event = FS_READABLE;
  321.         Bit_Clear(streamID, tempReadMask);
  322.  
  323.         if (Bit_IsSet(streamID, tempWriteMask)) {
  324.         event |= FS_WRITABLE;
  325.         Bit_Clear(streamID, tempWriteMask);
  326.         }
  327.         if (Bit_IsSet(streamID, tempExceptMask)) {
  328.         event |= FS_EXCEPTION;
  329.         Bit_Clear(streamID, tempExceptMask);
  330.         }
  331.  
  332.         if (infoPtr->eventMask & event) {
  333.         (*infoPtr->proc) (infoPtr->data, streamID, event);
  334.         }
  335.  
  336.         streamID = Bit_FindFirstSet(maxPossNumStreams, tempReadMask);
  337.     }
  338.  
  339.     streamID = Bit_FindFirstSet(maxPossNumStreams, tempWriteMask);
  340.     while (streamID != -1) {
  341.         if (streamID < 0 || streamID > MAX_NUM_STREAMS) {
  342.         panic("Fs_Dispatch: stream ID %d out of range\n", streamID);
  343.         }
  344.  
  345.         infoPtr = &(infoArray[streamID]);
  346.         event = FS_WRITABLE;
  347.         Bit_Clear(streamID, tempWriteMask);
  348.  
  349.         if (Bit_IsSet(streamID, tempExceptMask)) {
  350.         event |= FS_EXCEPTION;
  351.         Bit_Clear(streamID, tempExceptMask);
  352.         }
  353.  
  354.         if (infoPtr->eventMask & event) {
  355.         (*infoPtr->proc) (infoPtr->data, streamID, event);
  356.         }
  357.  
  358.         streamID = Bit_FindFirstSet(maxPossNumStreams, tempWriteMask);
  359.     }
  360.  
  361.     streamID = Bit_FindFirstSet(maxPossNumStreams, tempExceptMask);
  362.     while (streamID != -1) {
  363.         if (streamID < 0 || streamID > MAX_NUM_STREAMS) {
  364.         panic("Fs_Dispatch: stream ID %d out of range\n", streamID);
  365.         }
  366.  
  367.         infoPtr = &(infoArray[streamID]);
  368.         Bit_Clear(streamID, tempExceptMask);
  369.  
  370.         if (infoPtr->eventMask & FS_EXCEPTION) {
  371.         (*infoPtr->proc) (infoPtr->data, streamID, FS_EXCEPTION);
  372.         }
  373.  
  374.         streamID = Bit_FindFirstSet(maxPossNumStreams, tempExceptMask);
  375.     }
  376.     }
  377. }
  378.  
  379.  
  380. /*
  381.  *----------------------------------------------------------------------
  382.  *
  383.  * Fs_EventHandlerCreate --
  384.  *
  385.  *    Save the handler routine and data for a stream so it can be
  386.  *    called when the stream becomes ready in the main dispatch loop.
  387.  *    The handler "proc" should be declared as follows:
  388.  *
  389.  *    void
  390.  *    proc(clientData, streamID, eventMask)
  391.  *        ClientData    clientData;
  392.  *        int        streamID;
  393.  *        int        eventMask;
  394.  *    {
  395.  *    }
  396.  *
  397.  *    The association between a handler and a stream can be destroyed 
  398.  *    by calling Fs_EventHandlerDestroy.
  399.  *
  400.  * Results:
  401.  *    None.
  402.  *
  403.  * Side effects:
  404.  *    The infoArray and select masks are updated so events on the
  405.  *    stream will cause the proc routine to be called.
  406.  *
  407.  *----------------------------------------------------------------------
  408.  */
  409.  
  410. void
  411. Fs_EventHandlerCreate(streamID, eventMask, proc, clientData)
  412.     register int streamID;        /* Stream to watch. */
  413.     register int eventMask;        /* Events to watch for:  must be an
  414.                      * OR'ed combination of FS_READABLE,
  415.                      * FS_WRITABLE, or FS_EXCEPTION. */
  416.     void    (*proc)();        /* Procedure to call when an event in
  417.                      * eventMask occurs for streamID. */
  418.     ClientData    clientData;        /* Value to pass to proc. */
  419. {
  420.     if (!initialized) {
  421.     DispatcherInit();
  422.     }
  423.  
  424.     if (streamID < 0 || streamID > MAX_NUM_STREAMS) {
  425.     panic("Fs_EventHandlerCreate: stream ID %d out of range\n", streamID);
  426.     } 
  427.  
  428.     if ((eventMask & (FS_READABLE|FS_WRITABLE|FS_EXCEPTION)) == 0) {
  429.     panic("Fs_EventHandlerCreate: bad eventMask %x for stream %d\n", 
  430.         eventMask, streamID);
  431.     }
  432.  
  433.     if (infoArray[streamID].inUse) {
  434.     panic("Fs_EventHandlerCreate: stream ID %d already has a handler (0x%x)\n", 
  435.         streamID, infoArray[streamID].proc);
  436.     } 
  437.     infoArray[streamID].inUse = TRUE;
  438.     infoArray[streamID].proc = proc;
  439.     infoArray[streamID].data = clientData;
  440.     infoArray[streamID].eventMask = eventMask;
  441.  
  442.     if (eventMask & FS_READABLE) {
  443.     Bit_Set(streamID, readMask);
  444.     }
  445.     if (eventMask & FS_WRITABLE) {
  446.     Bit_Set(streamID, writeMask);
  447.     }
  448.     if (eventMask & FS_EXCEPTION) {
  449.     Bit_Set(streamID, exceptMask);
  450.     }
  451.  
  452.     /*
  453.      * Calculate the highest stream ID that's in use and add 1 to convert
  454.      * it into the # of bits in active use in the select masks.
  455.      */
  456.     if (streamID >= maxPossNumStreams) {
  457.     maxPossNumStreams = streamID + 1;
  458.     }
  459. }
  460.  
  461.  
  462. /*
  463.  *----------------------------------------------------------------------
  464.  *
  465.  * Fs_EventHandlerData --
  466.  *
  467.  *    Given an event ID for a stream, return the client data associated
  468.  *    with the event.
  469.  *
  470.  * Results:
  471.  *    The client data associated with the event.
  472.  *
  473.  * Side effects:
  474.  *    None.
  475.  *
  476.  *----------------------------------------------------------------------
  477.  */
  478.  
  479. ClientData
  480. Fs_EventHandlerData(streamID)
  481.     register int streamID;
  482. {
  483.     if (!initialized) {
  484.     panic("Fs_EventHandlerData: not initialized yet.\n");
  485.     }
  486.  
  487.     if (streamID < 0 || streamID > MAX_NUM_STREAMS) {
  488.     panic("Fs_EventHandlerData: stream ID %d out of range\n", streamID);
  489.     } 
  490.  
  491.     if (!infoArray[streamID].inUse) {
  492.     panic("Fs_EventHandlerData: stream ID %d not in use\n", streamID);
  493.     }
  494.  
  495.     return(infoArray[streamID].data);
  496. }
  497.  
  498.  
  499. /*
  500.  *----------------------------------------------------------------------
  501.  *
  502.  * Fs_EventHandlerChangeData --
  503.  *
  504.  *    Given an event ID for a stream, return the client data associated
  505.  *    with the event.
  506.  *
  507.  * Results:
  508.  *    The previous value of the client data associated with the event.
  509.  *
  510.  * Side effects:
  511.  *    None.
  512.  *
  513.  *----------------------------------------------------------------------
  514.  */
  515.  
  516. ClientData
  517. Fs_EventHandlerChangeData(streamID, newData)
  518.     register int streamID;
  519.     ClientData     newData;
  520. {
  521.     ClientData     oldData;
  522.  
  523.     if (!initialized) {
  524.     panic("Fs_EventHandlerChangeData: not initialized yet.\n");
  525.     }
  526.  
  527.     if (streamID < 0 || streamID > MAX_NUM_STREAMS) {
  528.     panic("Fs_EventHandlerChangeData: stream ID %d out of range\n", streamID);
  529.     } 
  530.  
  531.     if (!infoArray[streamID].inUse) {
  532.     panic("Fs_EventHandlerChangeData: stream ID %d not in use\n", streamID);
  533.     }
  534.  
  535.     oldData = infoArray[streamID].data;
  536.     infoArray[streamID].data = newData;
  537.     return(oldData);
  538. }
  539.  
  540.  
  541. /*
  542.  *----------------------------------------------------------------------
  543.  *
  544.  * Fs_EventHandlerDestroy --
  545.  *
  546.  *    Destroy the event handler routine for a stream.
  547.  *
  548.  * Results:
  549.  *    None.
  550.  *
  551.  * Side effects:
  552.  *    The infoArray and select masks are updated so events on
  553.  *    streamID will be ignored.
  554.  *
  555.  *----------------------------------------------------------------------
  556.  */
  557.  
  558. void
  559. Fs_EventHandlerDestroy(streamID)
  560.     register int streamID;
  561. {
  562.     register int eventMask;
  563.  
  564.     if (!initialized) {
  565.     panic("Fs_EventHandlerDestroy: not initialized yet.\n");
  566.     }
  567.  
  568.     if (streamID < 0 || streamID > MAX_NUM_STREAMS) {
  569.     panic("Fs_EventHandlerDestroy: stream ID %d out of range\n", streamID);
  570.     } 
  571.  
  572.     infoArray[streamID].inUse = FALSE;
  573.     eventMask = infoArray[streamID].eventMask;
  574.     infoArray[streamID].eventMask = 0;
  575.  
  576.     if (eventMask & FS_READABLE) {
  577.     Bit_Clear(streamID, readMask);
  578.     }
  579.     if (eventMask & FS_WRITABLE) {
  580.     Bit_Clear(streamID, writeMask);
  581.     }
  582.     if (eventMask & FS_EXCEPTION) {
  583.     Bit_Clear(streamID, exceptMask);
  584.     }
  585.  
  586.     /*
  587.      * Adjust maxPossNumStreams if this stream was the highest one in use.
  588.      */
  589.     if (streamID == (maxPossNumStreams - 1)) {
  590.     do {
  591.         maxPossNumStreams--;
  592.     } while ((maxPossNumStreams >= 1) && 
  593.             (!infoArray[maxPossNumStreams - 1].inUse));
  594.     }
  595. }
  596.  
  597.  
  598. /*
  599.  *----------------------------------------------------------------------
  600.  *
  601.  * Fs_TimeoutHandlerCreate --
  602.  *
  603.  *    Schedules a routine to be called at a certain time by adding 
  604.  *    it to the timer queue. 
  605.  *
  606.  *    When the client routine is called at its scheduled time, it is 
  607.  *    passed two parameters:
  608.  *     a) the clientData argument passed into this routine, and
  609.  *     b) the time it is scheduled to be called at.
  610.  *    Hence the routine should be declared as:
  611.  *
  612.  *        void
  613.  *        proc(clientData, time)
  614.  *        ClientData clientData;
  615.  *        Time time;
  616.  *        {}
  617.  *
  618.  *    The time a routine should be called at can be specified in two
  619.  *    ways: an absolute time (e.g. 12:00 4 July 1776) or an interval.
  620.  *    For example, to have proc called in 1 hour from now and every hour 
  621.  *    after that, the call to Fs_TimeoutHandlerCreate is:
  622.  *
  623.  *        Fs_TimeoutHandlerCreate(time_OneHour, TRUE, proc, clientData);
  624.  *
  625.  *      The 2nd argument (TRUE) to Fs_TimeoutHandlerCreate means the
  626.  *      routine will be called at the interval + the current time.
  627.  *      Routines scheduled with an interval will automatically be
  628.  *      rescheduled after they are called. They should deschedule themselves 
  629.  *      if they are not to be called more than once. Routines scheduled to
  630.  *      run at an absolute time will be called just once and must
  631.  *      reschedule themselves explicitly.
  632.  *
  633.  *    No error checking is done to make sure that a (proc, clientData) pair
  634.  *    is not put on the list more than once.
  635.  *
  636.  *
  637.  * Results:
  638.  *    A token that can be used to destroy the handler with 
  639.  *    Fs_TimeoutHandlerDestroy.
  640.  *
  641.  * Side effects:
  642.  *    The timeout queue is extended.
  643.  *
  644.  *----------------------------------------------------------------------
  645.  */
  646.  
  647. Fs_TimeoutHandler
  648. Fs_TimeoutHandlerCreate(time, relativeTime, proc, clientData)
  649.     Time     time;            /* See comments above. */
  650.     Boolean    relativeTime;
  651.     void    (*proc)();        /* Procedure to call. */
  652.     ClientData    clientData;        /* Value to pass to proc. */
  653. {
  654.     register TimeoutInfo *newPtr;
  655.     TimeoutInfo *itemPtr;
  656.     Boolean inserted;        /* TRUE if added to queue in FORALL loop. */
  657.  
  658.     if (!initialized) {
  659.     DispatcherInit();
  660.     }
  661.  
  662.     if (time.seconds == 0) {
  663.     /*
  664.      * Make sure the timeout value isn't too small so that we don't
  665.      * spend all our time calling timeout handlers.
  666.      */
  667.     if (time.microseconds < MIN_TIMEOUT) {
  668.         time.microseconds = MIN_TIMEOUT;
  669.     }
  670.     }
  671.  
  672.     newPtr = (TimeoutInfo *) malloc(sizeof(TimeoutInfo));
  673.  
  674.     if (relativeTime) {
  675.     /*
  676.      * Convert the interval into an absolute time by adding the 
  677.      * interval to the current time.
  678.      */
  679.     Time    curTime;
  680.  
  681.     (void) gettimeofday((struct timeval *) &curTime,
  682.         (struct timezone *) NULL);
  683.     Time_Add(curTime, time, &(newPtr->time));
  684.  
  685.     newPtr->interval = time;
  686.     newPtr->resched = TRUE;
  687.     } else {
  688.     newPtr->time = time;
  689.     newPtr->resched = FALSE;
  690.     }
  691.     newPtr->proc = proc;
  692.     newPtr->data = clientData;
  693.  
  694.     /*
  695.      *  Go through the timer queue and insert the new routine.  The queue
  696.      *  is ordered by the time field in the element.  The sooner the
  697.      *  routine needs to be called, the closer it is to the front of the
  698.      *  queue.  The new routine will not be added to the queue inside the
  699.      *  FOR loop if its scheduled time is after all elements in the queue
  700.      *  or the queue is empty.  It will be added after the last element in
  701.      *  the queue.
  702.      */
  703.  
  704.     inserted = FALSE;  /* assume new element not inserted inside FOR loop.*/
  705.     List_InitElement((List_Links *) newPtr);
  706.     LIST_FORALL(&timeoutList, (List_Links *) itemPtr) {
  707.  
  708.        if (Time_LT(newPtr->time, itemPtr->time)) {
  709.         List_Insert((List_Links *) newPtr, LIST_BEFORE(itemPtr));
  710.         inserted = TRUE;
  711.         break;
  712.     }
  713.     }
  714.     if (!inserted) {
  715.     List_Insert((List_Links *) newPtr, LIST_ATREAR(&timeoutList));
  716.     }
  717.  
  718.     return((Fs_TimeoutHandler) newPtr);
  719. }
  720.  
  721.  
  722. /*
  723.  *----------------------------------------------------------------------
  724.  *
  725.  * Fs_TimeoutHandlerDestroy --
  726.  *
  727.  *    Deschedules a routine that was to be called at a certain time 
  728.  *    by removing it from the timer queue.
  729.  *
  730.  *    It is not an error if the handler is not on the queue.
  731.  *
  732.  * Results:
  733.  *    None.
  734.  *
  735.  * Side effects:
  736.  *    The timer queue structure is updated. 
  737.  *
  738.  *----------------------------------------------------------------------
  739.  */
  740.  
  741. void
  742. Fs_TimeoutHandlerDestroy(token)
  743.     Fs_TimeoutHandler token;
  744. {
  745.     register List_Links *itemPtr;
  746.     TimeoutInfo *infoPtr = (TimeoutInfo *) token;
  747.  
  748.     if (!initialized) {
  749.     panic("Fs_TimeoutHandlerDestroy: not initialized yet.\n");
  750.     }
  751.  
  752.     if (infoPtr == (TimeoutInfo *) NULL) {
  753.     return;
  754.     }
  755.  
  756.     LIST_FORALL(&timeoutList, itemPtr) {
  757.  
  758.     if ((List_Links *) infoPtr == itemPtr) {
  759.         List_Remove(itemPtr);
  760.         break;
  761.     }
  762.     }
  763. }
  764.  
  765. /*
  766.  *----------------------------------------------------------------------
  767.  *
  768.  * CallTimeoutHandler --
  769.  *
  770.  *    Go through the timer queue and call the routines that are
  771.  *    scheduled to be called now.
  772.  *
  773.  * Results:
  774.  *    None.
  775.  *
  776.  * Side effects:
  777.  *    The called routine may cause side effects.
  778.  *
  779.  *----------------------------------------------------------------------
  780.  */
  781.  
  782. static void
  783. CallTimeoutHandler()
  784. {
  785.     register TimeoutInfo *readyPtr;    /* Ptr to handler that's ready to 
  786.                      * be called. */
  787.  
  788.     register TimeoutInfo *itemPtr;    /* Used to examine timeout queue. */
  789. #define itemListPtr    ((List_Links *) itemPtr)
  790.  
  791.     Time curTime;
  792.  
  793.     /*
  794.      *  The callback timer has expired. This means at least the first
  795.      *  routine on the timer queue is ready to be called.  Go through
  796.      *  the queue and call all routines that are scheduled to be
  797.      *  called. Since the queue is ordered by time, we can quit looking 
  798.      *  when we find the first routine that does not need to be called.
  799.      */
  800.  
  801.     if (!List_IsEmpty(&timeoutList)) {
  802.  
  803.     (void) gettimeofday((struct timeval *) &curTime,
  804.         (struct timezone *) NULL);
  805.  
  806.     itemListPtr = List_First(&timeoutList); 
  807.     while (!List_IsAtEnd(&timeoutList, itemListPtr)) {
  808.         if (Time_GT(itemPtr->time, curTime)) {
  809.         /*
  810.          * The first routine is not ready yet so all the other
  811.          * routines aren't ready either. We are done for now.
  812.          */
  813.          
  814.         break;
  815.  
  816.         } else {
  817.  
  818.         /*
  819.          * First remove the item before calling it. This allows 
  820.          * the routine to call Fs_TimeoutHandlerDestroy or
  821.          * Fs_TimeoutHandlerCreate without messing up the 
  822.          * list pointers.
  823.          */
  824.  
  825.         (List_Links *) readyPtr = itemListPtr;
  826.         itemListPtr  = List_Next(itemListPtr);
  827.         if (itemListPtr == NULL) {
  828.             panic(
  829.             "(FsDispatch)CallTimeoutHandler: next item == NULL\n");
  830.         }
  831.         List_Remove((List_Links *) readyPtr);
  832.  
  833.  
  834.         if (readyPtr->proc == NULL) {
  835.             panic("(FsDispatch)CallTimeoutHandler: routine == NULL\n");
  836.         } else {
  837.             (readyPtr->proc) (readyPtr->data, readyPtr->time);
  838.         }
  839.  
  840.         /*
  841.          * If the routine is to be called again automatically,
  842.          * compute the next time to run and insert it back in the
  843.          * queue. Otherwise, free the element because it isn't 
  844.          * needed any more.
  845.          */
  846.  
  847.         if (!(readyPtr->resched)) {
  848.             free((char *) readyPtr);
  849.         } else{
  850.             List_Links    *tempPtr;
  851.  
  852.             Time_Add(curTime, readyPtr->interval, &(readyPtr->time));
  853.  
  854.             tempPtr = List_First(&timeoutList);
  855.             while (!List_IsAtEnd((&timeoutList), tempPtr)) {
  856.             if (Time_GT( ((TimeoutInfo *)tempPtr)->time, 
  857.                     readyPtr->time)) {
  858.                 break;
  859.             }
  860.             tempPtr = List_Next(tempPtr);
  861.             }
  862.             List_InitElement((List_Links *) readyPtr);
  863.             List_Insert((List_Links *) readyPtr, LIST_BEFORE(tempPtr));
  864.         }
  865.         }
  866.     } /* while */
  867.     } 
  868. }
  869.  
  870.  
  871. /*
  872.  *----------------------------------------------------------------------
  873.  *
  874.  * ComputeTimeoutValue --
  875.  *
  876.  *    Compute when the first routine on the timeout queue needs to be 
  877.  *    called. The value returned in *timeoutPtr is the amount time
  878.  *    that must past before the first routine is called. If a routine
  879.  *    needs to be called now, we return TRUE, otherwise a return value
  880.  *    of FALSE means nothing's ready to be run yet.
  881.  *
  882.  * Results:
  883.  *    TRUE        - routine(s) on the timeout queue need to be run now.
  884.  *    FALSE        - nothing needs to be run yet.
  885.  *
  886.  * Side effects:
  887.  *    None.
  888.  *
  889.  *----------------------------------------------------------------------
  890.  */
  891.  
  892. static Boolean
  893. ComputeTimeoutValue(timeoutPtr)
  894.     Time    *timeoutPtr;
  895. {
  896.     Time    firstTime;
  897.     Time    curTime;
  898.  
  899.     if (List_IsEmpty(&timeoutList)) {
  900.     /*
  901.      * Nothing on the timeout queue so return a very long timeout value.
  902.      */
  903.  
  904.     *timeoutPtr = forever;
  905.     return(FALSE);
  906.  
  907.     } else {
  908.     (void) gettimeofday((struct timeval *) &curTime,
  909.         (struct timezone *) NULL);
  910.     firstTime = ((TimeoutInfo *) (List_Next(&timeoutList)))->time;
  911.  
  912.     if (Time_LE(firstTime, curTime)) {
  913.         /*
  914.          * The callback time of the first routine has already past.
  915.          */
  916.  
  917.         return(TRUE);
  918.     } else {
  919.         /*
  920.          * The callback time of the first routine is in the future.
  921.          */
  922.  
  923.         Time_Subtract(firstTime, curTime, timeoutPtr);
  924.         return(FALSE);
  925.     }
  926.     }
  927. }
  928.